package com.virjar.hermes.hermesadmin.controller;

import com.google.common.base.Charsets;
import com.virjar.hermes.hermesadmin.dao.AgentApkMapper;
import com.virjar.hermes.hermesadmin.model.AgentApk;
import com.virjar.hermes.hermesadmin.model.CommonRes;
import com.virjar.hermes.hermesadmin.util.CommonUtil;
import com.virjar.hermes.hermesadmin.util.Constant;
import com.virjar.hermes.hermesadmin.util.ReturnUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import net.dongliu.apk.parser.bean.ApkMeta;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * Created by virjar on 2018/9/4.<br>
 * 上传agent的apk
 */
@Slf4j
@RestController
@RequestMapping("/agentAPK")
public class AgentApkController {


    @Resource
    private AgentApkMapper agentApkMapper;

    //获取上传的文件夹，具体路径参考application.properties中的配置
    @Value("${web.upload-path}")
    private String uploadPath;

    /**
     * @param file 上传一个apk
     */
    @ApiOperation(value = "上传apk文件", notes = "这个apk,是Hermes Agent编译后产生的apk,HermesAgent 在客户端安装之后,发现有更新后,将会自动拉去最新版本的apk,apk大小限制,不超过500M")
    @PostMapping(value = "/upload")
    @ResponseBody
    public CommonRes<String> uploadAgentAPK(@RequestParam("agentAPK") MultipartFile file) {
        String srcFileName = file.getOriginalFilename();
        if (!StringUtils.endsWithIgnoreCase(srcFileName, ".apk")) {
            return ReturnUtil.failed("the upload file must has .apk suffix");
        }

        File dir = genAgentApkUploadPath();
        String filedName = DateTime.now().toString("yyyy_MM_dd") + "_" + System.currentTimeMillis() + "_" + Thread.currentThread().getId() + ".apk";
        File targetFile = new File(dir, filedName);
        log.info("save new agent apk file to :{}", targetFile.getAbsoluteFile());
        try {
            file.transferTo(targetFile);
        } catch (IOException e) {
            log.error("failed to save agent apk filed", e);
            return ReturnUtil.failed(e);
        }

        //now parse the file
        ApkMeta apkMeta = CommonUtil.parseApk(targetFile);
        if (!StringUtils.equalsIgnoreCase(Constant.agentApkPackage, apkMeta.getPackageName())) {
            //只能上传agent的apk文件,其他文件,认为是不合法的
            return ReturnUtil.failed("agent apk package must be: " + Constant.agentApkPackage);
        }

        AgentApk agentApk = new AgentApk();
        agentApk.setEnabled(false);
        agentApk.setSavePath(targetFile.getAbsolutePath());
        agentApk.setVersion(apkMeta.getVersionName());
        agentApk.setVersionCode(apkMeta.getVersionCode().intValue());
        agentApk.setApkPackage(apkMeta.getPackageName());

        agentApkMapper.insert(agentApk);
        return ReturnUtil.success("upload success");
    }


    private File genAgentApkUploadPath() {
        File dir = new File(uploadPath);
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                throw new RuntimeException("failed to create upload directory: " + dir.getAbsolutePath());
            }
        }

        File agentApk = new File(dir, "agent_apk");
        if (agentApk.isDirectory()) {
            return agentApk;
        }
        if (!agentApk.mkdir()) {
            throw new RuntimeException("failed to create agent apk file upload directory: " + agentApk.getAbsolutePath());
        }
        return agentApk;
    }

    /**
     * list the last 10 record,to manage recent apks,enable/disable agent,or rollback deployment
     *
     * @return the highest versionCode records,max size is 10
     */
    @ApiOperation(value = "查看apk列表", notes = "这里只展示10条记录,且根据apk版本倒排,因为我们只会对最近发布的apk进行操作")
    @GetMapping(value = "/recent")
    @ResponseBody
    public CommonRes<List<AgentApk>> recent() {
        return ReturnUtil.success(agentApkMapper.recent());
    }

    @ApiOperation(value = "操作某个版本的apk上线或者下线", notes = "这里不限制在10条记录内,所以留了一个后门")
    @GetMapping(value = "/enable")
    @ResponseBody
    public CommonRes<String> enable(@RequestParam("apkId") Integer apkId, @RequestParam("enable") boolean enable) {
        AgentApk agentApk = agentApkMapper.selectByPrimaryKey(apkId);
        if (agentApk == null) {
            return ReturnUtil.failed("record not find");
        }
        agentApk.setEnabled(enable);
        agentApkMapper.updateByPrimaryKey(agentApk);
        return ReturnUtil.success("success");
    }

    @ApiOperation(value = "下载某个agentApk", notes = "这个是给Android内的agent访问的,他可以通过这个接口下载到新的apk代码,并完成自更新")
    @GetMapping("/download")
    public ResponseEntity<InputStreamResource> downloadFile(@RequestParam("apkId") Integer apkId)
            throws IOException {
        AgentApk agentApk = agentApkMapper.selectByPrimaryKey(apkId);
        if (agentApk == null) {
            throw new IllegalStateException("record not find");
        }
        File savePath = new File(agentApk.getSavePath());
        if (!savePath.exists() || !savePath.isFile() || !savePath.canRead()) {
            throw new IllegalStateException("resource can not access");
        }
        FileSystemResource file = new FileSystemResource(savePath);
        HttpHeaders headers = new HttpHeaders();
        headers.setCacheControl("no-cache, no-store, must-revalidate");
        headers.add("Content-Disposition", "attachment; filename=" + new String((Constant.agentApkPackage + "_" + agentApk.getVersionCode() + ".apk").getBytes(Charsets.UTF_8), Charsets.ISO_8859_1));
        headers.setPragma("no-cache");
        headers.setExpires(0);
        return ResponseEntity
                .ok()
                .headers(headers)
                .contentLength(file.contentLength())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(new InputStreamResource(file.getInputStream()));
    }

}
